///////////////////////////
//
// File: bcf_menu_histogram.cpp
//
// Author: Brian Fehrman
//
// Purpose: Contains the class implementation for the Histogram menu functions which include such functions
//          display historgram, auto-stretch, equalize, etc.
//
///////////////////////////

#include "bcf_img_proc.h"

//////////////////////
// Function: Create Histogram
// Author: Brian Fehrman
//
// Purpose: Takes an image and generates a histogram
///////////////////////
void BCF_Img_Proc::Create_Histogram( const Image &image, std::vector<uint> &histogram)
{
    //Create vector whose size is the number of pixel values
    histogram.clear();
    histogram.resize( NUM_PIX_VALS );

    for( int curr_idx = 0; curr_idx < NUM_PIX_VALS; curr_idx++ )
    {
        histogram[ curr_idx ] = 0;
    }

    int nrows = image.Height();
    int ncols = image.Width();

    //Create histogram
    for( int curr_r = 0; curr_r < nrows; curr_r++ )
    {
        for( int curr_c = 0; curr_c < ncols; curr_c++ )
        {
            histogram[ image[ curr_r ][ curr_c ] ]++;
        }
    }
}

//////////////////////
// Function: Create CDF
// Author: Brian Fehrman
//
// Purpose: Takes a histogram and generates a CDF
///////////////////////
void BCF_Img_Proc::Create_CDF( const Image &image, std::vector<uint> &histogram, std::vector<float> &CDF, const float total_pixels)
{
    //Create vector whose size is the number of pixel values
    CDF.clear();
    CDF.resize( NUM_PIX_VALS );

    float scale = ( NUM_PIX_VALS - 1.0 ) / total_pixels;

    CDF[ 0 ] = histogram[ 0 ] * scale;

    for( int curr_idx = 1; curr_idx < NUM_PIX_VALS; curr_idx++ )
    {
       CDF[ curr_idx ] = CDF[ curr_idx - 1 ]  + histogram[ curr_idx ] * scale + 0.5;
    }
}

//////////////////////
// Function: Determine Percentile
// Author: Brian Fehrman
//
// Purpose: Takes a histogram of an image and a percentile value between 0 and 1. It will
//          then return the pixel value which corresponds to the lower bound of that percentile
///////////////////////
int BCF_Img_Proc::Determine_Percentile( const std::vector<uint> &histogram, float percentile )
{
    float curr_percentile = 0.0;
    int curr_tally = 0;
    int percentile_pix_val = 0;
    int total_pixels = 0;

    //Get total number of pixels
    for( int curr_p = 0; curr_p < NUM_PIX_VALS; curr_p++ )
    {
        total_pixels += histogram[ curr_p ];
    }

    //Find percentile pixel value
    while( ( percentile_pix_val  < NUM_PIX_VALS ) && ( curr_percentile < percentile ) )
    {
        curr_tally += histogram[ percentile_pix_val ];
        curr_percentile = float( curr_tally ) / total_pixels;
        percentile_pix_val++;
    }

    return percentile_pix_val;
}

//////////////////////
// Function: Contrast Stretch
// Author: Brian Fehrman using Dr. John Weiss histogramStretch function
//
// Purpose: Takes an image and performs a histogram based contrast stretch.
///////////////////////
bool BCF_Img_Proc::Menu_Histogram_Contrast_Stretch( Image &image )
{
    //No point in doing anything if we don't have an image with which to work.
    if ( image.IsNull() ) return false;

    histogramStretch( image );

    //Return true in order to update the image
    return true;
}

//////////////////////
// Function: Contrast Stretch (Specify)
// Author: Brian Fehrman
//
// Purpose: Lets the user specify the lower and upper percentile of pixel
//          intensity values to ignore in the histogram based contrast stretch
/////////////////////////
bool BCF_Img_Proc::Menu_Histogram_Contrast_Stretch_Specify( Image &image )
{
    //No point in doing anything if we don't have an image with which to work.
    if ( image.IsNull() ) return false;

    int lower_percentile = 0;
    int upper_percentile = 100;

    //Get user end points for histogram based contrast adjustment
    Dialog dlg("Adjust Contrast");
    dlg.Add( lower_percentile, "Lower Percentile", 0, 100);
    dlg.Add( upper_percentile, "Upper Percentile", 0, 100);

    if( dlg.Show())
    {
       // BCF_Img_Proc::Menu_Histogram_Contrast_Stretch( image, lower_percentile / 100.0, upper_percentile / 100.0 );
    }
    else
    {
        return false;
    }

    return true;
}

//////////////////////
// Function: Display Histogram
// Author: Brian Fehrman using Dr. John Weiss's displayHistogram function
//
// Purpose: Displays the histogram for an image using the displayHistogram function
///////////////////////
bool BCF_Img_Proc::Menu_Histogram_Display_Histogram(Image &image)
{
    //No point in doing anything if we don't have an image with which to work.
    if ( image.IsNull() ) return false;

    displayHistogram( image );

    return true;
}

//////////////////////
// Function: Histogram Equalize
// Author: Brian Fehrman using Dr. John Weiss histogramEqualize function
//
// Purpose: Perfoms histogram equlization
///////////////////////
bool BCF_Img_Proc::Menu_Histogram_Equalize( Image &image )
{
    //No point in doing anything if we don't have an image with which to work.
    if ( image.IsNull() ) return false;

    histogramEqualize( image );

    return true;
}

//////////////////////
// Function: Histogram Equalize Clip
// Author: Brian Fehrman
//
// Purpose: Perfoms histogram equlization by using the CDF as a mapping
//          function. Clips the histogram before finding the CDF.
///////////////////////
bool BCF_Img_Proc::Menu_Histogram_Equalize_Clip( Image &image )
{
    //No point in doing anything if we don't have an image with which to work.
    if ( image.IsNull() ) return false;

    double threshold = 1.0;
    int nrows = image.Height();
    int ncols = image.Width();
    float total_pix_orig = nrows * ncols;
    int total_pix_new = 0;
    unsigned int cut_off_pix = 1;
    std::vector< uint > histogram;
    std::vector< float > CDF;

    //Get user end points for histogram based contrast adjustment
    Dialog dlg("Specify Threshold");
    dlg.Add( threshold, "Threshold" );

    if( dlg.Show())
    {
        cut_off_pix = total_pix_orig * ( threshold / 100.0 ) + 0.5;

        BCF_Img_Proc::Create_Histogram( image, histogram);

        //Clip histogram values
        for( int curr_idx = 0; curr_idx < NUM_PIX_VALS; curr_idx++ )
        {
            if( histogram[ curr_idx ] > cut_off_pix )
            {
                histogram[ curr_idx ] = cut_off_pix;
            }

            total_pix_new += histogram[ curr_idx ];
        }

        BCF_Img_Proc::Create_CDF( image, histogram, CDF, total_pix_new );

        //Update image intensity values
        for( int curr_r = 0; curr_r < nrows; curr_r++ )
        {
            for( int curr_c = 0; curr_c < ncols; curr_c++ )
            {
                image[ curr_r ][ curr_c ] = CDF[ image[ curr_r ][ curr_c ] ];
            }
        }
    }
    else
    {
        return false;
    }

    return true;
}
